package main

import (
	"context"
	"errors"
	"fmt"
)

type (
	invoker     func(ctx context.Context, req, resp interface{}) error
	interceptor func(ctx context.Context, req, resp interface{}, ivk invoker) error
)

var invokers = make([]invoker, 0)

func intercept(ctx context.Context, req, resp interface{}, interceptors []interceptor, ivk invoker) error {
	if len(interceptors) == 0 {
		return ivk(ctx, req, resp)
	}
	ivk2 := getInvoker(0, interceptors, ivk)

	fmt.Printf("ivk2: %p\n", ivk2)
	return interceptors[0](ctx, req, resp, ivk2)
}

func getInvoker(cur int, interceptors []interceptor, ivk invoker) invoker {
	fmt.Printf("ivk: %p\n", ivk)
	if cur == len(interceptors)-1 {
		return ivk
	}
	return func(ctx context.Context, req, resp interface{}) error {
		return interceptors[cur+1](ctx, req, resp, getInvoker(cur+1, interceptors, ivk))
	}
}

func main() {

	ivk := func(ctx context.Context, req, resp interface{}) error {
		fmt.Println("I'm in")
		if req.(string) == "hi" || req.(string) == "hello" {
			fmt.Println("Hi")
			return nil
		}
		return errors.New("please say hi to me")
	}

	cep1 := func(ctx context.Context, req, resp interface{}, ivk invoker) error {
		fmt.Println("no.1 interceptor")
		return ivk(ctx, req, resp)

	}

	cep2 := func(ctx context.Context, req, resp interface{}, ivk invoker) error {
		fmt.Println("no.2 interceptor")
		return ivk(ctx, req, resp)
	}

	cep3 := func(ctx context.Context, req, resp interface{}, ivk invoker) error {
		fmt.Println("no.3 interceptor")
		return ivk(ctx, req, resp)
	}

	ceps := make([]interceptor, 0)
	ceps = append(ceps, cep1, cep2, cep3)

	err := intercept(context.TODO(), "hi", "hi", ceps, ivk)

	fmt.Printf("last ivk: %p\n", ivk)

	if err != nil {
		panic(err)
	}
}
